package com.java.jvalidator.framework.validator;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import com.java.jvalidator.framework.TypeUtil;
import com.java.jvalidator.framework.ValidationException;
import com.java.jvalidator.framework.config.Parameters;

public class EnumValidator extends AbstractValidator {
	private static Validator instance = new EnumValidator();

	public static Validator getInstance() {
		return instance;
	}

	protected boolean beginValidate(Object target, String fieldName, Parameters param) throws ValidationException {
		Object fieldValue = TypeUtil.getPropertyValue(target, fieldName);
		if (fieldValue == null) {
			return true;
		}

		String valuesString = param.getParameterValueAsString("values");
		if (TypeUtil.isStringNullOrEmpty(valuesString)) {
			throw new ValidationException("Error occurs,the validation parameter is invalid!");
		}
		String[] array = valuesString.split(",");
		Collection<String> values = Arrays.asList(array);

		String containsString = param.getParameterValueAsString("contains");
		boolean contains = false;

		if (TypeUtil.isStringNullOrEmpty(containsString))
			contains = true;
		else {
			contains = containsString.equalsIgnoreCase("true");
		}
		if ((fieldValue instanceof String))
			return values.contains(fieldValue.toString()) == contains;
		if ((fieldValue instanceof Date))
			return dateContains(fieldValue, values, param.getParameterValueAsString("dateFormat")) == contains;
		if (TypeUtil.isDigital(fieldValue))
			return numberContains(fieldValue, values) == contains;
		if ((fieldValue instanceof Enum)) {
			return enumContains(fieldValue, values) == contains;
		}
		throw new ValidationException("Sorry,This type isn't support!");
	}

	private boolean enumContains(Object fieldValue, Collection<String> values) throws ValidationException {
		Class<?> enumClass = fieldValue.getClass();
		Object[] enumConstants = enumClass.getEnumConstants();
		List<String> enumString = new ArrayList<String>();
		for (Object enumItem : enumConstants) {
			enumString.add(enumItem.toString());
		}
		for (String enumValue : values) {
			if (!enumString.contains(enumValue)) {
				throw new ValidationException("The enum value error!There isn't such a enum!");
			}
		}
		return values.contains(fieldValue.toString());
	}

	private boolean dateContains(Object fieldValue, Collection<String> values, String dateFormat) throws ValidationException {
		Date value = (Date) fieldValue;
		SimpleDateFormat formatter = null;
		if (TypeUtil.isStringNullOrEmpty(dateFormat))
			formatter = new SimpleDateFormat();
		else {
			try {
				formatter = new SimpleDateFormat(dateFormat);
			} catch (NullPointerException e) {
				throw new ValidationException(e);
			} catch (IllegalArgumentException e) {
				throw new ValidationException(e);
			}
		}
		String valueStr = formatter.format(value);
		return values.contains(valueStr);
	}

	private boolean numberContains(Object value, Collection<String> values) throws ValidationException {
		Double doubleValue = Double.valueOf(TypeUtil.convertNumberToDouble(value));
		List<Double> collec = new ArrayList<Double>();
		for (String item : values) {
			try {
				collec.add(Double.valueOf(Double.parseDouble(TypeUtil.trimString(item))));
			} catch (NumberFormatException e) {
				throw new ValidationException(e);
			}
		}
		return collec.contains(doubleValue);
	}
}
